<?php
/*
 * Created on 2012-4-25
 * @iChying
 * To change the template for this generated file go to
 * Window - Preferences - PHPeclipse - PHP - Code Templates
 */
	class ExtBase64Compression {
		private $encode_string;
		private $decode_string;

		// array
		private $encode_table;
		private $decode_table;
		private $decode_hex;
		private $encode_hex;
		private $dec_to_hex;

		public function init() {
			$this->generate_encode_table();
			$this->generate_decode_table();
			$this->generate_encode_hex_table();
			$this->generate_decode_hex_table();
		}

		private function generate_encode_table() {
			$this->encode_table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		}

		// preprocess the decode table
		private function generate_decode_table() {
			// preprocess the bit operation;
			function gen_array($value) {
				$bit_val1 = $value;
				$bit_val2 = ($value & 0x3c) >> 2;
				$bit_val3 = ($value & 0x03) << 2;
				$bit_val4 = ($value & 0x30) >> 4;
				$bit_val5 = $value & 0x0f;
				$bit_val6 = ($value & 0x3c) >> 2;

				return array($bit_val1, $bit_val2, $bit_val3, $bit_val4, $bit_val5, $bit_val6);
			}

			$this->decode_table = array();

			// A-Z
			for ($i = 0; $i <= 25; ++$i) {
 				$_array = gen_array($i);
 				$this->decode_table[chr(ord('A') + $i)] = $_array;
 			}

			// a-z
 			for ($i = 26, $j = 0; $i <= 51; ++$i, ++$j) {
 				$_array = gen_array($i);
 				$this->decode_table[chr(ord('a') + $j)] = $_array;
 			}

			// 0-9
 			for ($i = 52, $j = 0; $i <= 61; ++$i, ++$j) {
 				$_array = gen_array($i);
 				$this->decode_table[chr(ord('0') + $j)] = $_array;
 			}

 			// +
 			$_array = gen_array(62);
 			$this->decode_table['+'] = $_array;

			// /
			$_array = gen_array(63);
 			$this->decode_table['/'] = $_array;

 			#var_dump($this->decode_table);
		}

		// preprocess the encode table
		private function generate_encode_hex_table() {
			// preprocess the bit operation
			function gen_array2($value) {
				$bit_val0 = $value;
				$bit_val1 = $value << 2;
				$bit_val2 = ($value & 0x0c) >> 2;
				$bit_val3 = ($value & 0x03) << 4;

				return array($bit_val0, $bit_val1, $bit_val2, $bit_val3);
			}

			$this->encode_hex = array();

			// 0-9
			for ($i = 0; $i < 10; ++$i) {
				$_array = gen_array2($i);
				$this->encode_hex[$i] = $_array;
			}

			// a-f
			for ($i = 10; $i < 16; ++$i) {
				$_array = gen_array2($i);
				$this->encode_hex[$i] = $_array;
			}

			#var_dump($this->encode_hex);
		}

		// preprocess the Unicode to UTF-8 table
		private function generate_decode_hex_table() {
			$this->decode_hex = array();
			$this->dec_to_hex = array();

			// 0-9
			for ($i = 0; $i <= 9; ++$i) {
				$this->dec_to_hex[] = chr(ord('0') + $i);

				$_value1 = ($i & 0x0c) >> 2;
				$_value2 = ($i & 0x03);
				$_array = array(($_value1 | 0x08), ($_value2 << 2), $_value1, ($_value2 | 0x08));

				$this->decode_hex[chr((ord('0') + $i))] = $_array;
			}

			// a-f
			for ($i = 10, $j = 0; $i <= 15; ++$i, ++$j) {
				$this->dec_to_hex[] = chr(ord('a') + $j);

				$_value1 = ($i & 0x0c) >> 2;
				$_value2 = ($i & 0x03);
				$_array = array(($_value1 | 0x08), ($_value2 << 2), $_value1, ($_value2 | 0x08));

				$this->decode_hex[chr((ord('a') + $j))] = $_array;
			}

			#var_dump($this->dec_to_hex);
			#var_dump($this->hex_table);
		}

		// utf-8(%xx%xx%xx) convert to Unicode(xxxx)
		private function UTF8ToUnicode($str) {
			$unicode_str = '';
			$str_length = strlen($str);

			for ($i = 0; $i < $str_length; $i += 9) {
				$str1 = hexdec(substr($str, $i + 1, 2)) & 0x0F;
				$str2 = hexdec(substr($str, $i + 4, 2)) & 0x3F;
				$str3 = hexdec(substr($str, $i + 7, 2)) & 0x3F;

				$dec_str = ($str1 << 12) | ($str2 << 6) | $str3;

				$unicode_str .= dechex($dec_str);
			}

			return $unicode_str;
		}

		// compress the Chinese character
		public function compress($str) {
			$this->encode_string = "";

			$utf8_str = urlencode($str);
			$php_encode_str = $this->UTF8ToUnicode($utf8_str);

			$str_length = strlen($php_encode_str);

			for ($i = 0; $i < $str_length; $i += 4) {
				$ch1 = hexdec($php_encode_str[$i]);
				$ch2 = hexdec($php_encode_str[$i + 1]);
				$ch3 = hexdec($php_encode_str[$i + 2]);
				$ch4 = hexdec($php_encode_str[$i + 3]);

				$num_1 = ($this->encode_hex[$ch1][1]) | ($this->encode_hex[$ch2][2]);
				$num_2 = ($this->encode_hex[$ch2][3]) | ($this->encode_hex[$ch3][0]);
				$num_3 = $this->encode_hex[$ch4][1];

				//none preprocess
				/*
				 $num_1 = ($ch1 << 2) | (($ch2 & 0x0c) >> 2);
				 $num_2 = (($ch2 & 0x03) << 4) | $ch3;
				 $num_3 = $ch4 << 2;
				 */

				// xxxx xx11 111x xxxx[00]
				if (($ch2 & 3) == 3 && $ch3 >= 14) {
					$num_2 = ($num_2 & 0x37); // 11011x
					$num_3 = ($num_3 | 0x1); // xxxx01
				}

				#echo $num_1, ' ', $num_2, ' ', $num_3, "<br />";

				$encode_string .= $this->encode_table[$num_1];
				$encode_string .= $this->encode_table[$num_2];
				$encode_string .= $this->encode_table[$num_3];
			}

			return $encode_string;
		}

		public function decompress($encode_string) {
			$unicode = "";
			$hex_alphabet = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f');
			$encode_str_length = strlen($encode_string);

			for ($i = 0; $i < $encode_str_length; $i += 3) {
				$ch1 = $encode_string[$i];
				$ch2 = $encode_string[($i + 1)];
				$ch3 = $encode_string[($i + 2)];

				$base_value2 = $this->decode_table[$ch2][0];
				$base_value3 = $this->decode_table[$ch3][0];

				if ($base_value3 & 0x1) {
					$base_value2 = $base_value2 | 0x08;
					$ch2 = $this->encode_table[$base_value2];
				}

				$de_idx1 = $this->decode_table[$ch1][1];
				$de_idx2 = ($this->decode_table[$ch1][2]) | ($this->decode_table[$ch2][3]);
				$de_idx3 = $this->decode_table[$ch2][4];
				$de_idx4 = $this->decode_table[$ch3][5];

				// none preprocess
				/*
				 $base_value1 = $this->decode_table[$ch1][0];
				 $base_value2 = $this->decode_table[$ch2][0];
				 $base_value3 = $this->decode_table[$ch3][0];

				 if ($base_value3 & 0x1) {
					$base_value2 = $base_value2 | 0x08;
				 }

				 $de_idx1 = ($base_value1 & 0x3c) >> 2;
				 $de_idx2 = (($base_value1 & 0x03) << 2) | (($base_value2 & 0x30) >> 4);
				 $de_idx3 = $base_value2 & 0x0f;
				 $de_idx4 = ($base_value3 & 0x3c) >> 2;
				 */

				$unicode .= ($hex_alphabet[$de_idx1].$hex_alphabet[$de_idx2].$hex_alphabet[$de_idx3].$hex_alphabet[$de_idx4]);
			}

			$unicode = $this->UCSToUTF8($unicode);
			$utf8 = urldecode($unicode);

			return $utf8;
		}

		/*
		 * convert to utf8 encode
		 * just use for chinese character
		 * unicode range 0x0800~0xffff
		 * utf-8 format:
		 * 1000 xxxx
		 * 10xx xxxx
		 * 10xx xxxx
		 */
		private function UCSToUTF8($unicode) {
			$unicode_length = strlen($unicode);
			$UTF8 = "";

			// four character one process
			for ($i = 0; $i < $unicode_length; $i += 4) {
				$utf8_part1 = 'e'.$unicode[$i];

				$ch2 = $unicode[($i + 1)];
				$ch3 = $unicode[($i + 2)];

				// bit operate in table
				$idx_1 = $this->decode_hex[$ch2][0];
				$idx_2 = ($this->decode_hex[$ch2][1]) | ($this->decode_hex[$ch3][2]);
				$idx_3 = $this->decode_hex[$ch3][3];

				// dechex in table
				$utf8_part2 = $this->dec_to_hex[$idx_1].$this->dec_to_hex[$idx_2];
				$utf8_part3 = $this->dec_to_hex[$idx_3].$unicode[($i + 3)];

				// %xx%xx%xx
				$UTF8 .= ("%".$utf8_part1).("%".$utf8_part2).("%".$utf8_part3);
			}

			return $UTF8;
		}
	}
?>